/*
 * Data structures for 2.xBSD filesystem.
 *
 * Copyright (C) 2006-2014 Serge Vakulenko, <serge@vak.ru>
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and this
 * permission notice and warranty disclaimer appear in supporting
 * documentation, and that the name of the author not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * The author disclaim all warranties with regard to this
 * software, including all implied warranties of merchantability
 * and fitness.  In no event shall the author be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether
 * in an action of contract, negligence or other tortious action,
 * arising out of or in connection with the use or performance of
 * this software.
 */
#define BSDFS_BSIZE             1024    /* block size */
#define BSDFS_ROOT_INODE        2       /* root directory in inode 2 */
#define BSDFS_LOSTFOUND_INODE   3       /* lost+found directory in inode 3 */
#define BSDFS_SWAP_INODE        4       /* swap file in inode 4 */
#define BSDFS_INODES_PER_BLOCK  16      /* inodes per block */

#define NICINOD         32              /* number of superblock inodes */
#define NICFREE         200             /* number of superblock free blocks */

/*
 * 28 of the di_addr address bytes are used; 7 addresses of 4
 * bytes each: 4 direct (4Kb directly accessible) and 3 indirect.
 */
#define NDADDR  4                       /* direct addresses in inode */
#define NIADDR  3                       /* indirect addresses in inode */
#define NADDR   (NDADDR + NIADDR)       /* total addresses in inode */

/*
 * NINDIR is the number of indirects in a file system block.
 */
#define NINDIR  (DEV_BSIZE / sizeof(daddr_t))
#define NSHIFT  8                       /* log2(NINDIR) */
#define NMASK   0377L                   /* NINDIR - 1 */

/*
 * The path name on which the file system is mounted is maintained
 * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in
 * the super block for this name.
 */
#define MAXMNTLEN   28

#define FSMAGIC1    ('F' | 'S'<<8 | '<'<<16 | '<'<<24)
#define FSMAGIC2    ('>' | '>'<<8 | 'F'<<16 | 'S'<<24)

typedef struct {
    const char      *filename;
    int             fd;
    unsigned long   seek;
    int             writable;
    int             dirty;              /* sync needed */
    int             modified;           /* write_block was called */
    unsigned char   part_type;
    unsigned        part_offset;
    unsigned        part_nsectors;

    unsigned        isize;              /* size in blocks of superblock + I list */
    unsigned        fsize;              /* size in blocks of entire volume */
    unsigned        swapsz;             /* size in blocks of swap area */
    unsigned        nfree;              /* number of in core free blocks (0-100) */
    unsigned        free [NICFREE];     /* in core free blocks */
    unsigned        ninode;             /* number of in core I nodes (0-100) */
    unsigned        inode [NICINOD];    /* in core free I nodes */
    unsigned        flock;              /* lock during free list manipulation */
    unsigned        ilock;              /* lock during I list manipulation */
    unsigned        fmod;               /* super block modified flag */
    unsigned        ronly;              /* mounted read-only flag */
    time_t          utime;              /* current date of last update */
    unsigned        tfree;              /* total free blocks */
    unsigned        tinode;             /* total free inodes */
    char            fsmnt [MAXMNTLEN];  /* ordinary file mounted on */
    unsigned        lasti;              /* start place for circular search */
    unsigned        nbehind;            /* est # free inodes before s_lasti */
    unsigned        flags;              /* mount time flags */
} fs_t;

typedef struct {
    fs_t            *fs;
    unsigned        number;
    int             dirty;              /* save needed */

    unsigned short  mode;               /* file type and access mode */
#define INODE_MODE_FMT      0170000     /* type of file */
#define INODE_MODE_FCHR      020000     /* character special */
#define INODE_MODE_FDIR      040000     /* directory */
#define INODE_MODE_FBLK      060000     /* block special */
#define INODE_MODE_FREG     0100000     /* regular */
#define INODE_MODE_FLNK     0120000     /* symbolic link */
#define INODE_MODE_FSOCK    0140000     /* socket */
#define INODE_MODE_SUID       04000     /* set user id on execution */
#define INODE_MODE_SGID       02000     /* set group id on execution */
#define INODE_MODE_SVTX       01000     /* save swapped text even after use */
#define INODE_MODE_READ        0400     /* read, write, execute permissions */
#define INODE_MODE_WRITE       0200
#define INODE_MODE_EXEC        0100

    unsigned short  nlink;              /* directory entries */
    unsigned        uid;                /* owner */
    unsigned        gid;                /* group */
    unsigned long   size;               /* size */
    unsigned        addr [7];           /* device addresses constituting file */
    unsigned        flags;              /* user defined flags */
/*
 * Super-user and owner changeable flags.
 */
#define USER_SETTABLE   0x00ff          /* mask of owner changeable flags */
#define USER_NODUMP     0x0001          /* do not dump file */
#define USER_IMMUTABLE  0x0002          /* file may not be changed */
#define USER_APPEND     0x0004          /* writes to file may only append */
/*
 * Super-user changeable flags.
 */
#define SYS_SETTABLE    0xff00          /* mask of superuser changeable flags */
#define SYS_ARCHIVED    0x0100          /* file is archived */
#define SYS_IMMUTABLE   0x0200          /* file may not be changed */
#define SYS_APPEND      0x0400          /* writes to file may only append */

    time_t          atime;              /* time last accessed */
    time_t          mtime;              /* time last modified */
    time_t          ctime;              /* time created */
} fs_inode_t;

typedef struct {
    unsigned        ino;
    unsigned        reclen;
    unsigned        namlen;
    char            name [63+1];
} fs_dirent_t;

typedef void (*fs_directory_scanner_t) (fs_inode_t *dir,
    fs_inode_t *file, char *dirname, char *filename, void *arg);

typedef struct {
    fs_inode_t      inode;
    int             writable;           /* write allowed */
    unsigned long   offset;             /* current i/o offset */
} fs_file_t;

typedef enum {
    INODE_OP_LOOKUP,                    /* lookup inode by name */
    INODE_OP_CREATE,                    /* create new file */
    INODE_OP_DELETE,                    /* delete file */
    INODE_OP_LINK,                      /* make a link to a file */
} fs_op_t;

int fs_seek (fs_t *fs, unsigned long offset);
int fs_read8 (fs_t *fs, unsigned char *val);
int fs_read16 (fs_t *fs, unsigned short *val);
int fs_read32 (fs_t *fs, unsigned *val);
int fs_write8 (fs_t *fs, unsigned char val);
int fs_write16 (fs_t *fs, unsigned short val);
int fs_write32 (fs_t *fs, unsigned val);

int fs_read (fs_t *fs, unsigned char *data, int bytes);
int fs_write (fs_t *fs, unsigned char *data, int bytes);

int fs_open (fs_t *fs, const char *filename, int writable, unsigned pindex);
void fs_close (fs_t *fs);
int fs_set_partition (fs_t *fs, unsigned pindex);
int fs_sync (fs_t *fs, int force);
int fs_create (fs_t *fs, const char *filename, int kbytes,
    unsigned swap_kbytes);
int fs_check (fs_t *fs);
void fs_print (fs_t *fs, FILE *out);

int fs_mount(fs_t *fs, char *dirname);

int fs_inode_get (fs_t *fs, fs_inode_t *inode, unsigned inum);
int fs_inode_save (fs_inode_t *inode, int force);
void fs_inode_clear (fs_inode_t *inode);
void fs_inode_truncate (fs_inode_t *inode, unsigned long size);
void fs_inode_print (fs_inode_t *inode, FILE *out);
int fs_inode_read (fs_inode_t *inode, unsigned long offset,
    unsigned char *data, unsigned long bytes);
int fs_inode_write (fs_inode_t *inode, unsigned long offset,
    unsigned char *data, unsigned long bytes);
int fs_inode_alloc (fs_t *fs, fs_inode_t *inode);
int fs_inode_by_name (fs_t *fs, fs_inode_t *inode, const char *name,
    fs_op_t op, int mode);
int fs_inode_lookup (fs_t *fs, fs_inode_t *inode, const char *name);
int fs_inode_create (fs_t *fs, fs_inode_t *inode, const char *name, int mode);
int fs_inode_delete (fs_t *fs, fs_inode_t *inode, const char *name);
int fs_inode_link (fs_t *fs, fs_inode_t *inode, const char *name, int mode);
int inode_build_list (fs_t *fs);

int fs_write_block (fs_t *fs, unsigned bnum, unsigned char *data);
int fs_read_block (fs_t *fs, unsigned bnum, unsigned char *data);
int fs_block_free (fs_t *fs, unsigned int bno);
int fs_block_alloc (fs_t *fs, unsigned int *bno);
int fs_indirect_block_free (fs_t *fs, unsigned int bno, int nblk);
int fs_double_indirect_block_free (fs_t *fs, unsigned int bno, int nblk);
int fs_triple_indirect_block_free (fs_t *fs, unsigned int bno, int nblk);

void fs_directory_scan (fs_inode_t *inode, char *dirname,
    fs_directory_scanner_t scanner, void *arg);
void fs_dirent_pack (unsigned char *data, fs_dirent_t *dirent);
void fs_dirent_unpack (fs_dirent_t *dirent, unsigned char *data);

int fs_file_create (fs_t *fs, fs_file_t *file, const char *name, int mode);
int fs_file_open (fs_t *fs, fs_file_t *file, const char *name, int wflag);
int fs_file_read (fs_file_t *file, unsigned char *data,
    unsigned long bytes);
int fs_file_write (fs_file_t *file, unsigned char *data,
    unsigned long bytes);
int fs_file_close (fs_file_t *file);
